﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Neat;
using Neat.Components;
using Neat.Mathematics;
using Neat.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Sectors
{
    public class Wall : LineSegment
    {
        public NeatGame Game;
        public string Name = "generic";
        
        string _texture = "generic_wall",
            _textureB = "generic_wall",
            _textureU = "generic_wall",
            _textureL = "generic_wall";

        public string Texture {
            get { return _texture;}
            set { _texture = value;
            if (model != null) model.Texture = _texture;
            }
        }
        public string TextureB {
            get { return _textureB;}
            set { _textureB = value; if (backModel != null) backModel.Texture = _textureB; }
        }

        public string TextureU
        {
            get { return _textureU; }
            set { _textureU = value; if (upperModel != null) upperModel.Texture = upperModel.TextureB = _textureU; }
        }

        public string TextureL
        {
            get { return _textureL; }
            set { _textureL = value; if (lowerModel != null) lowerModel.Texture = lowerModel.TextureB = _textureL; }
        }

        public Sector SectorA;
        public Sector SectorB;

        WallMesh model, backModel, upperModel, lowerModel,
            upperModel2, lowerModel2;

        public enum WallTypes
        {
            Passable,
            Window,
            Normal,
            Invisible //only show upper and lower walls; use to connect the sectors
        }

        WallTypes _wallType = WallTypes.Normal;
        public WallTypes WallType { get { return _wallType; } set { _wallType = value; } }
        public Action<Wall> OnPushAction;
        public Action<Wall> OnShotAction;
        public Action<Wall> OnPassAction;
        public string OnPushScript;
        public string OnShotScript;
        public string OnPassScript;

        Vector2 
            _textureUV = Vector2.Zero,
            _textureBUV = Vector2.Zero,
            _textureUUV = Vector2.Zero,
            _textureLUV = Vector2.Zero;

        public Vector2 TextureUV
        {
            get { return _textureUV; }
            set
            {
                _textureUV = value;
                if (model != null) model.UV = value;
            }
        }
        public Vector2 TextureBUV
        {
            get { return _textureBUV; }
            set
            {
                _textureBUV = value;
                if (backModel != null) backModel.UV = value;
            }
        }
        public Vector2 TextureUUV
        {
            get { return _textureUUV; }
            set
            {
                _textureUUV = value;
                if (upperModel != null) { upperModel.UV = upperModel2.UV = value; }
            }
        }
        public Vector2 TextureLUV
        {
            get { return _textureLUV; }
            set
            {
                _textureLUV = value;
                if (lowerModel != null) { lowerModel.UV = lowerModel2.UV = value; }
            }
        }

        public Wall(NeatGame game, LineSegment edge, Sector parent)
            : base(edge)
        {
            this.Game = game;
            console = game.Console;
            SectorA = parent;
            CreateModel();
        }

        public Wall(LineSegment edge, Sector parent)
            : base(edge)
        {
            SectorA = parent;
        }

        public Wall() : base()
        {
        }

        public static Wall FromWall(Wall w, LineSegment edge)
        {
            var result = (Wall)w.MemberwiseClone();
            result.StartPos = edge.StartPos;
            result.EndPos = edge.EndPos;
            return result;
        }

        public void CreateModel()
        {
            if (SectorA == null && SectorB == null)
            {
                if (console != null)
                    console.WriteLine("Wall '" + Name + "' is not connected to any sectors. CreateModel() terminated.");
                return;
            }
            float maxFloor, minCeiling;
            if (SectorA != null && SectorB == null) 
            {
                maxFloor = SectorA.FloorHeight;
                minCeiling = SectorA.CeilingHeight;
            }
            else if (SectorB != null && SectorA == null)
            {
                maxFloor = SectorB.FloorHeight;
                minCeiling = SectorB.CeilingHeight;
            }
            else
            {
                maxFloor = MathHelper.Max(SectorA.FloorHeight, SectorB.FloorHeight);
                minCeiling = MathHelper.Min(SectorA.CeilingHeight, SectorB.CeilingHeight);
                bool sectorAMin = SectorA.CeilingHeight == minCeiling;
                bool sectorAMax = SectorA.FloorHeight == maxFloor;
                //U&L Walls should be drawn on the taller side.
                upperModel = new WallMesh(Game, (sectorAMin ? new LineSegment(this.EndPos,this.StartPos) : this), TextureU, minCeiling, (sectorAMin ? SectorB.CeilingHeight : SectorA.CeilingHeight), TextureUUV);
                upperModel2 = new WallMesh(Game, (sectorAMin ? this : new LineSegment(this.EndPos, this.StartPos)), TextureU, minCeiling, (sectorAMin ? SectorB.CeilingHeight : SectorA.CeilingHeight), TextureUUV);
                upperModel.TextureB = upperModel2.TextureB = upperModel.Texture;
                lowerModel = new WallMesh(Game, (sectorAMax ? new LineSegment(this.EndPos,this.StartPos) : this), TextureL, (sectorAMax ? SectorB.FloorHeight : SectorA.FloorHeight), maxFloor,TextureLUV);
                lowerModel2 = new WallMesh(Game, (sectorAMax ? this :new LineSegment(this.EndPos, this.StartPos)), TextureL, (sectorAMax ? SectorB.FloorHeight : SectorA.FloorHeight), maxFloor, TextureLUV);
                lowerModel.TextureB = lowerModel.TextureB = lowerModel.Texture;
            }
            model = new WallMesh(Game, this, Texture, maxFloor, minCeiling, TextureUV);
            if (!string.IsNullOrEmpty(TextureB))
                backModel = new WallMesh(Game, new LineSegment(EndPos, StartPos),
                    TextureB, maxFloor, minCeiling, TextureBUV);
        }

        public void Render(BasicEffect ef)
        {
            if (model != null && WallType != WallTypes.Invisible) model.Draw(ef);
            if (SectorB != null && !string.IsNullOrEmpty(TextureB) &&
                backModel != null && WallType != WallTypes.Invisible) backModel.Draw(ef);
            if (SectorA != null && SectorB != null)
            {
                if (upperModel != null) { upperModel.Draw(ef); upperModel2.Draw(ef); }
                if (lowerModel != null) { lowerModel.Draw(ef); lowerModel2.Draw(ef); }
            }
        }

        #region Console
        Neat.Components.Console console;
        public void AttachToConsole(Neat.Components.Console console)
        {
            this.console = console;
            console.AddCommand("ww_name", ww_name);
            console.AddCommand("ww_texture1", ww_texture);
            console.AddCommand("ww_texture", ww_texture);
            console.AddCommand("ww_texture2", ww_textureB);
            console.AddCommand("ww_uppertexture", ww_textureU);
            console.AddCommand("ww_lowertexture", ww_textureL);
            console.AddCommand("ww_textureu", ww_textureU);
            console.AddCommand("ww_texturel", ww_textureL);
            console.AddCommand("ww_textureuv", ww_textureUV);
            console.AddCommand("ww_type", ww_type);
            console.AddCommand("ww_pushed", ww_push);
            console.AddCommand("ww_shot", ww_shot);
            console.AddCommand("ww_passed", ww_passed);
            console.AddCommand("ww_create", ww_create);
        }

        void ww_textureUV(IList<string> args)
        {
            Vector2 uv = GeometryHelper.String2Vector(console.Args2Str(args, 2));
            switch (args[1].ToLower()[0])
            {
                case 'f':
                    TextureUV = uv;
                    break;
                case 'b':
                    TextureBUV = uv;
                    break;
                case 'u':
                    TextureUUV = uv;
                    break;
                case 'l':
                    TextureLUV = uv;
                    break;
            }
        }
        void ww_name(IList<string> args)
        {
            Name = args[1];
        }
        void ww_texture(IList<string> args)
        {
            Texture = args[1];
        }
        void ww_textureB(IList<string> args)
        {
            TextureB = args[1];
        }
        void ww_textureU(IList<string> args)
        {
            TextureU = args[1];
        }
        void ww_textureL(IList<string> args)
        {
            TextureL = args[1];
        }
        Dictionary<string, WallTypes> wallTypesLookupTable = new Dictionary<string, WallTypes>(){
            {"passable", WallTypes.Passable},
            {"window", WallTypes.Window},
            {"normal", WallTypes.Normal},
            {"invisible", WallTypes.Invisible}};
        void ww_type(IList<string> args)
        {
            var arg = args[1].ToLower();
            if (wallTypesLookupTable.ContainsKey(arg)) WallType = wallTypesLookupTable[arg];
            else
            {
                int i = -1;
                if (int.TryParse(arg, out i))
                {
                    WallType = (WallTypes)(i);
                }
                else
                    console.WriteLine("Invalid Wall Type.");
            }
        }
        void ww_push(IList<string> args)
        {
            OnPushScript = console.Args2Str(args, 1);
        }
        void ww_shot(IList<string> args)
        {
            OnShotScript = console.Args2Str(args, 1);
        }
        void ww_passed(IList<string> args)
        {
            OnPassScript = console.Args2Str(args, 1);
        }
        void ww_create(IList<string> args)
        {
            CreateModel();
        }
        #endregion
    }
}
